#!/bin/bash  
#Author: YangYi
#Date: 2024-10-22
#Version: v1

#Author: YangYi
#Date: 2024-10-29
#Version: v2
#Modify Global Version



#脚本运行时间统计
startTime=`date +%Y%m%d-%H:%M:%S`
startTime_s=`date +%s`

# 文件过滤路径
FILTER_FILE=/app/paasadmin/
sudo /bin/mkdir -p  $FILTER_FILE 

# 输出结果保存路径
quotaResult="$FILTER_FILE/quotaResult.text"

# 临时文件路径，用于存储处理后的内容  
quotaResultTmp="${quotaResult}.tmp"

# 数据初始化
JsonData=""
metadata=""

# 确保安装了jq  
if ! command -v jq &> /dev/null  
then  
    echo "jq could not be found, please install it first."  
    exit  
fi 


# json数据获取
function GetJson(){

  # 获取Kubernetes API服务器的地址和端口  
  # API_SERVER=$(kubectl config view --raw -o json | jq -r '.clusters[0].cluster.server' | awk -F "//" '{print $2}')  
  API_URL=$(kubectl config view --raw -o json | jq -r '.clusters[0].cluster.server')
  # API_HOST=$(echo $API_SERVER | awk -F':' '{print $1}')  
  # API_PORT=$(echo $API_SERVER | awk -F':' '{print $2}')  

  # 获取用于访问API的token  
  TOKEN=$(kubectl describe secret $(kubectl get secret | grep ^default | cut -f1 -d ' ') | grep -E '^token' | cut -f2 -d':' | tr -d '\t' | tr -d ' ')

  # 初始化一个空的JSON数组来存储所有命名空间的Quota数据  
  QUOTAS_JSON="{\"items\": []}"

  # 获取所有命名空间  
  NAMESPACES=$(kubectl get namespaces -o jsonpath='{.items[*].metadata.name}')

  # 遍历每个命名空间并获取其Quota数据  
  for NS in $NAMESPACES
    do
      # 获取当前命名空间的Quota数据  
      QUOTA_DATA=$(curl -s -k -H "Authorization: Bearer $TOKEN" "$API_URL/api/v1/namespaces/$NS/resourcequotas")

      # 如果获取到的数据不是空的，则将其添加到JSON数组中  
      if [ "$QUOTA_DATA" != "{}" ]; then
          # 使用jq将新的Quota数据追加到JSON数组中  
          QUOTAS_JSON=$(echo $QUOTAS_JSON | jq --argjson new_quota "$QUOTA_DATA" '.items += [$new_quota.items[]]')
      fi
   done

  # 最终的JSON结果  
  JsonData=`echo $QUOTAS_JSON | jq -r '.[]'`

}

# json元数据处理
function TraversalJsonData(){

  metadata=`echo  "$JsonData" | jq -r '.[] | "\(.metadata.namespace) \(.status.used."requests.cpu") \(.status.used."requests.memory") \(.status.used."limits.cpu") \(.status.used."limits.memory")  \(.status.hard."limits.cpu") \(.status.hard."limits.memory")"'`
  
  echo "" > $quotaResult

  # 使用echo和管道处理metadata变量的内容  
  echo "$metadata" | while IFS= read -r line; do

    # 使用awk分割出第一列和其他列  
    first_column=$(echo "$line" | awk '{print $1}')
    other_columns=$(echo "$line" | awk '{$1=""; print substr($0,2)}' | xargs)  # 删除第一列，保留后续列，并去除前导空格  

    # 使用正则表达式去除第一列中的指定前缀和后缀  
    #processed_first_column=$(echo "$first_column" | sed -E 's/^(prod-|stage-|de-|ie-|se-|train-)|(-sys|-app)$//g')
    processed_first_column=$(echo "$first_column" | sed -E 's/(-sys|-app)$//g')

    # 输出处理后的第一列和之后的所有列  
    echo "$processed_first_column $other_columns" | grep -v "0B" >> $quotaResult 

  done

}


# 将数字从m/Mi转换为G  
function convert_to_G() {
    local value="$1"
    # 提取数字和单位  
    local num=$(echo "$value" | grep -oE '[0-9]+')
    local unit=$(echo "$value" | grep -oE '[a-zA-Z]+')

    # 判断单位并进行转换  
    if [[ "$unit" == "m" || "$unit" == "Mi" ]]; then
        # 将m/Mi转换为G (1G = 1024m)  
        echo "scale=3; $num / 1024" | bc
    else
        # 如果不是m或Mi，原样返回  
        echo "$value"  
    fi
}


# 数据处理
function dataInformation() {

  # 检查文件是否存在并且可读  
  if [[ ! -f "$quotaResult" || ! -r "$quotaResult" ]]; then
    echo "错误: 文件 $quotaResult 不存在或无法读取"  
    exit 1
  fi
 
  # 逐行读取文件并处理  
  while IFS= read -r line; do
    # 使用awk分割行中的列  
    read -ra columns <<< "$line"

    # 检查是否至少有五列  
    if [[ ${#columns[@]} -ge 7 ]]; then
        # 逐列处理第二、第三、第四、第五列  
        for i in {1..6}; do
            original_value="${columns[i]}"
            converted_value=$(convert_to_G "$original_value")
            columns[i]="$converted_value"
        done

        # 输出处理后的行  
        echo "${columns[*]}" >> $quotaResultTmp  
    else
        # 如果列数少于五列，输出原始行  
        echo "$line"  
    fi
  done < "$quotaResult"

  FileDispose

  sleep 1

}


# 去除结尾G和Gi
function removalGAndGi() {
  
  FileCheck
 
  # 使用awk处理文件内容  
    awk '  
    {  
      # 检查是否有足够的列  
      if (NF >= 5) {  
          # 去除第二列中的G或Gi  
          $2 = gensub(/[Gg]i$/, "", 1, $2);  

          # 去除第三列中的G或Gi  
          $3 = gensub(/[Gg]i$/, "", 1, $3);  

          # 去除第四列中的G或Gi  
          $4 = gensub(/[Gg]i$/, "", 1, $4);  
          
          # 去除第五列中的G或Gi  
          $5 = gensub(/[Gg]i$/, "", 1, $5);  

          # 去除第六列中的G或Gi  
          $6 = gensub(/[Gg]i$/, "", 1, $6);  

          # 去除第七列中的G或Gi  
          $7 = gensub(/[Gg]i$/, "", 1, $7);  
      }  
      
      # 打印处理后的行  
      print $0;  
    }' "$quotaResult" > "${quotaResult}.tmp"  
  
  # 检查awk命令是否成功执行并生成了临时文件  
  if [ $? -ne 0 ] || [ ! -s "${quotaResult}.tmp" ]; then  
      echo "错误：处理文件内容时出错。"  
      exit 1  
  fi  
   
  FileDispose
  sleep 1
}


# 浮点判断
function FloatingPoint() {
  
 FileCheck 
  
# 逐行读取源文件，并使用awk处理第2和第3列  
  while IFS= read -r line; do  
    # 使用awk处理行内容，将结果保存到临时文件中  
    echo "$line" | awk '  
    {  
        # 对第2列进行处理  
        if ($2 ~ /^\./) {  
            $2 = "0" $2;  
        }  
        # 对第3列进行处理  
        if ($3 ~ /^\./) {  
            $3 = "0" $3;  
        }  
        # 对第4列进行处理  
        if ($4 ~ /^\./) {  
            $4 = "0" $4;  
        }  
        # 对第5列进行处理  
        if ($5 ~ /^\./) {  
            $5 = "0" $5;  
        }  
        # 对第5列进行处理  
        if ($6 ~ /^\./) {  
            $6 = "0" $6;  
        }  
        # 对第5列进行处理  
        if ($7 ~ /^\./) {  
            $7 = "0" $7;  
        }  
        # 打印修改后的行  
        print $0;  
    }' >> "${quotaResult}.tmp"  
  done < "$quotaResult"  
  
  FileDispose
  sleep 1

}


# 数据汇总结果
function DataSummaryResult() {

  # 获取当前时间戳  
  timestamp=$(date +%Y%m%d%H%M%S)  
  
  # 定义输出文件路径  
  output_file="quota_result_$timestamp.csv"  
  
  # 使用awk处理文件内容，按第一列分组并求和第二至第五列  
  awk '  
    BEGIN {  
        # 打印表头  
        print "应用名,requests_cpu,requests_memory,limits_cpu,limits_memory,hard_limits_cpu,hard_limits_memory" > "'$FILTER_FILE$output_file'";  
    }  
    {  
        # 将第二至第五列数字按第一列名字累加到数组中  
        sums_cpu[$1] += $2;        
        sums_memory[$1] += $3;    
        limits_cpu[$1] += $4;       
        limits_memory[$1] += $5;    
        hard_limits_cpu[$1] += $6; 
        hard_limits_memory[$1] += $7; 
    }  
    END {  
        # 遍历数组，打印结果到文件  
        for (name in sums_cpu) {  
            # 四舍五入保留整数  
            rounded_cpu = sprintf("%.0f", sums_cpu[name]);  
            rounded_memory = sprintf("%.0f", sums_memory[name]);  
            rounded_ued_cpu = sprintf("%.0f", limits_cpu[name]);  
            rounded_ued_memory = sprintf("%.0f", limits_memory[name]);  
            rounded_hard_cpu = sprintf("%.0f", hard_limits_cpu[name]);  
            rounded_hard_memory = sprintf("%.0f", hard_limits_memory[name]);  
          
            # 打印名字和对应的和，以CSV格式，并追加到文件  
            print name "," rounded_cpu "," rounded_memory "," rounded_ued_cpu  "," rounded_ued_memory  "," rounded_hard_cpu "," rounded_hard_memory >> "'$FILTER_FILE$output_file'";  
        }  
     }  
  ' "$quotaResult"  
  
  # 输出结果文件路径  
  echo "结果已保存到 $FILTER_FILE$output_file"
}


# 临时文件处理
function FileDispose() {
   # 将临时文件内容覆盖回原始文件  
   mv "${quotaResult}.tmp" "$quotaResult"
   # 检查mv命令是否成功执行  
   if [ $? -ne 0 ]; then
      echo "错误：覆盖原始文件时出错。"  
      exit 1
   fi
}


# 临时文件检查
function FileCheck() {
  # 检查文件是否存在  
  if [ ! -f "$quotaResult" ]; then
     echo "文件 $quotaResult 不存在。"  
     exit 1
  fi

  # 检查文件是否为空  
  if [ ! -s "$quotaResult" ]; then
    echo "文件 $quotaResult 为空。"  
    exit 1
  fi
}


echo "-------------------------------------程序运行开始-------------------------------------"

echo "1. 获取集群数据Namespaces..."
GetJson
echo "2. 通过Namespaces获取ResourceQuotas..."
TraversalJsonData
removalGAndGi
dataInformation
echo "3. 数据格式转换完成..."
FloatingPoint
echo "4. 数据统计完成..."
DataSummaryResult

echo "-------------------------------------程序运行结束-------------------------------------"


endTime=`date +%Y%m%d-%H:%M:%S`
endTime_s=`date +%s`
sumTime=$[ $endTime_s - $startTime_s ]
echo "$startTime ---> $endTime" "程序运行时间: $sumTime seconds"













